/***********************Boot stages************************************************/
#include<stdlib.h>
#include <string.h>

#include <Kernel.h>

#define  Buffersize      (40)
#define  DRIVER_PERIOD   (60)

Kern* kern;
char CUR_DATA_DISK='A';

/*Main DRIVER funciton*/
int DRIVER_Action(IOReq* _IO);
/*Support driver function*/
int Sch_Return2readyQ(IOReq* _IO);
int closing_readed_file(IOReq* _IO);
size_t setFDlistinTask(File_D* fd);
/*Help funciton to system call open*/
void FILEproducing(size_t _fd_index);
/*Help funciton to system call open*/
size_t PreparetoReading(char* _filename);
/*System call*/
int readfile(char*_filename);
/*Support function to readfile*/

/************************************************System_Calls*************************************************/
size_t setFDlistinTask(File_D* fd)
{
    size_t index;
    for(index=0;kern->ST->running->FD[index];++index);/*looking for 1st NON zero index*/
    kern->ST->running->FD[index]=fd;
    return index;
}

void FILEproducing(size_t _fd_index)
{
    var_t*     tempvar=((var_t*)(kern->ST->running->userdata));
    tempvar->fdnumber=_fd_index;
    tempvar->if_file_open=opened;/*file open*/
    tempvar->buffer[Buffersize]=0;
}

size_t PreparetoReading(char* _filename)
{
    err_MM err1;
     size_t fd_index;/*here the openg process*/
        /*file descriptor is struct ot control status of open file*/
    MMheap* taskheap=kern->ST->running->MM_Man;

    File_D* fd=MM_Malloc(taskheap,sizeof(File_D),INOUT  &err1);
    if(err1)
    {
        return ST_OPEN_FILe_MEMORY_PROBLEMS;
    }

    strcpy(fd->name,_filename);
    fd->pos=0;
    /*set fd of open file into File_Descriptor table of runnitn Task*/
    fd_index=setFDlistinTask(fd);
    /*updating of user_data var_t*/
    FILEproducing(fd_index);
    return fd_index;
}

/*File descriptor can be fined from index, buffer and how much laready readed*/
int readfile(char* _filename)
{
    err_Sch     err;
    IOReq* IO=calloc(1,sizeof(IOReq));
    var_t* tempvar=(var_t*)(kern->ST->running->userdata);
    kern->ST->running->is_reschdeldular=notready;
    IO->buf=tempvar->buffer;
    IO->indexFD=(tempvar)->fdnumber;
    IO->lenght_of_open_file=Buffersize;/* to controll the end of file*/
    IO->n_readed=0;
    IO->Task=kern->ST->running;
    /*inserting IOreq to blockque*/
    if(!(err=Sch_InsertTask_to_blockQ(kern->ST->blockQ,IO)))
    {
            ++kern->ST->blockQ_size;
    }
    else
    {exit(err);}

return 0;
}

int openfile(char* _filename)
{
    size_t          fd_index_opened_file;
    var_t* tempvar=(var_t*)(kern->ST->running->userdata);
     if(tempvar->if_file_open)/*if file was open it must be read*/
    {
#ifdef _Debug_of_Schelduller
    if(tempvar->if_file_open && tempvar->fdnumber==0)
    {
        printf("\n openfile is ok. readfile tesing in progress");
    }
    else
    {
        exit(KERNEL_DEBUG_OPENFILE_PROBLEMS);
    }
#endif /*_Debug_of_Schelduller*/
        readfile(_filename);
    }
    else /*opening file for reading*
      1)Creat FiLe_Descriptor struct in runnign Task, as real opening process
      2)Put pointer to it in Task FD fild
      3)accept fd of open file=Get the index
      4)Put index in userdata(var_t)
      5)Change status of  is file open to open("1") for ativation read function on Next run
      6)For testing print the data in buffer to control the driver work, ad 1st time put '\0'
    {*/
        /*here the openg process*/
    fd_index_opened_file=PreparetoReading(_filename);
    return fd_index_opened_file ;/*returning the fd index of open file*/
}

/*******************************Driver Part******************************************************************/


int closing_readed_file(IOReq* _IO)
{
    var_t * tempvar=(var_t*)(_IO->Task->userdata);
    /*need to free all memory allocated by FD of task TASK*/
    MM_Free(_IO->Task->MM_Man,_IO->Task->FD[_IO->indexFD]);
    (_IO->Task->FD[_IO->indexFD])=0; /* need in this exicize;*/
    tempvar->if_file_open=closed;
    tempvar->fdnumber=-1;
    return 0;
}

int Sch_Return2readyQ(IOReq* _IO)
{
        closing_readed_file(_IO);/*Update the user var_data and free the FileDescriptor*/
        ++CUR_DATA_DISK;/*chagnging letter*/
        _IO->Task->is_reschdeldular=ready;
        Sch_InsertTask_inRightplace_to_Queue(kern->ST->readyQ,&(kern->ST->readyQ_size),_IO->Task);
        free(_IO);
        return 0;
}


int DRIVER_Action(IOReq* _IO)
{
    size_t      i;
    var_t*      tempvar= (var_t*)(_IO->Task->userdata);
#ifdef _Debug_of_Schelduller
    if(_IO->n_readed==0 && _IO->lenght_of_open_file==Buffersize && _IO->indexFD==0 && _IO->debugcount==0)
    {
        printf("\nOpen_read funciton is o.k");
    }
    printf("\n IO processed on task with Task PID                  %d",_IO->Task->taskID );
    printf("\nLetters readed from file are %d ,lenght_of_open_file %d ,",_IO->n_readed,_IO->lenght_of_open_file);
    printf("\nindex of FD table %d , times of Driver accesing the File %d .",_IO->indexFD,_IO->debugcount);
#endif /*_Debug_of_Schelduller*/
    for(i=0;i<IOBUFSIZE && (_IO->n_readed+i)<= _IO->lenght_of_open_file;++i)
    {
        tempvar->buffer[_IO->n_readed+i]=CUR_DATA_DISK;

       /* _IO->buf[i]=tempvar->buffer[_IO->n_readed+i];*/
    }
    tempvar->buffer[_IO->n_readed+i]=0;
#ifdef _Debug_of_Schelduller
     ++_IO->debugcount;
    printf("\nAfter  %d driver accecing of file , the data existed in IO Buffer is:\n %s",_IO->debugcount,_IO->buf);
    _IO->n_readed+=i;
    (_IO->Task->FD)[_IO->indexFD]->pos=_IO->n_readed;/*Updating the data inside of File Descriptor*/
#endif /*_Debug_of_Schelduller*/

/*Return the IOreq into blockQ*/
    if(_IO->n_readed!=_IO->lenght_of_open_file)
    {
       Sch_InsertTask_to_blockQ(kern->ST->blockQ,_IO);/*maybe i need to perform compariosn of existed IO in blockQ*/
        ++kern->ST->blockQ_size;
    }
    else
    {/*Return Task to ReadyQ*/
        Sch_Return2readyQ(_IO);
    }
    return 0;
}

int DRIVER(void)
{
    IOReq*       tempIOreq;

    printf("\n I am DRIVER don't interrupt me\n");
    if(kern->ST->blockQ_size)
    {
        PQ_Queue_Popfront(kern->ST->blockQ,OUT (void**) &tempIOreq);
        --kern->ST->blockQ_size;
        DRIVER_Action(tempIOreq);
    }
    return 0;
}
/********************************************************Activation of system*********************/
/*initial activation*/
err_Kern Kernel_boot()
{
    kern=malloc(sizeof(Kern));
    kern->ST    =malloc(sizeof(Sch_Schel));
    if (kern!=0 && kern->ST!=0)
    {
        Sch_Initiate(kern->ST,DRIVER,DRIVER_PERIOD);
        if(kern->ST==0)
        {
            return 0;
        }
        return KERNEL_SCHELD_INITIATION_PROBLEMS;
    }
        return KERNEL_NOT_CREATED ;
}

err_Kern Kern_RunSchel()
{
    int i;
    for(i=0;i<200;++i)
    {
        if(kern->ST->STfunc(kern->ST))
        {
            break;
        }
    }

    if (i==200)
    {
        return 0;
    }
    return SCHELDULER_PROBLEMS;


    /*return ST_Runforever();*/
}

err_Kern Kernel_SchelduleEvery(func _somefunc,size_t _periond,void* _userdata)
{
    err_Kern err;
    if (kern==0)
    {
        return KERNEL_NOT_ACCEPTED;
    }

    if( _somefunc==0)
    {
        return KERNEL_FUNCTION_NOT_ACCEPTED;
    }
    if(_periond<=1 && _periond>=10000)
    {
        return KERNEL_WRONG_PERIOD;
    }
    err=(err_Kern)Sch_InsertTask(kern->ST,_somefunc,_periond,_userdata);
    if(err)
    {
        return err;
    }
    return 0;
}

err_Kern Kernel_initdata(var_t** _userdata)
{
        if(_userdata==0)
    {
        return ST_EMPTY_USER_DATA_SENDING;
    }

    (*_userdata)=malloc(sizeof(var_t));
    if(*_userdata==0)
    {
        return ST_ALLOCATION_USERDATA_PROBLRMS;
    }
    (*_userdata)->if_file_open=closed;
    (*_userdata)->fdnumber=-1;/*initial values is negative*/
    return 0;
}

int Kern_Shutdown()
{
    Sch_Destroy(kern->ST);
    free(kern);
    return 0;
}

